//
//  NetworkTools.swift
//  新浪微博
//
//  Created by waterfoxjie on 15/7/31.
//  Copyright © 2015年 zj. All rights reserved.
//

import UIKit
import AFNetworking

// 重命名回调的方法，加强程序的刻度性
typealias NetWorkFinishedBack = (result : [String : AnyObject]? , error : NSError?)->()

// 定义一个错误类别标记
private let ErrorDomainName = "cn.zj.error.network"

// MARK: - 使用枚举封装错误信息
// swift中枚举跟类差不多，可以定义为任意的类型，也可以定义函数跟属性
private enum ZJNetworkError : Int {

    case networkDataError = -1
    
    case networkTokenError = -2
    
    // 定义属性
    private var networkErrorInfo : String {
    
        switch self {
        
        case.networkDataError : return "数据访问错误，没有数据~~"
            
        case.networkTokenError : return "Token为空~~"
            
        }
    }
    
    // 定义函数
    private func error() -> NSError {
    
        // domain：错误类别标记   code：错误代号，通常都是负数   userInfo：附加的错误信息，字典的形式
        return NSError(domain: ErrorDomainName, code: rawValue, userInfo: [ErrorDomainName : networkErrorInfo])
    }
    
}


// MARK: - 使用枚举封装网络请求类型
private enum RequestType : String {

    case GET = "GET"
    
    case POST = "POST"
    
}


class NetworkTools: AFHTTPSessionManager {
    
    // MARK: - 创建属性
    // client_id : 498939534
    // AppSecret : 4e52e58f2a405809a2e643dab5b3f5b6
    // redirect_uri : http://www.baidu.com
    // 请求授权url : https://api.weibo.com/oauth2/authorize
    private let clientId  = "498939534"
    
    private let appSecret = "4e52e58f2a405809a2e643dab5b3f5b6"
    
    let redirectUri = "http://www.baidu.com"
    
    
    // MARK: - 创建一个单例
    static let shareNetworkTools : NetworkTools = {
    
        // 获取新浪微博的访问地址
        let url = NSURL(string: "https://api.weibo.com/")
        
        let tools = NetworkTools(baseURL: url)
        
        // 设置解析数据类型
        tools.responseSerializer.acceptableContentTypes = NSSet(objects: "application/json", "text/json", "text/javascript", "text/plain") as Set<NSObject>
        
        return tools
    
    }()
    
    // MARK: - 创建一个方法，返回授权地址
    func oathuUrl() -> NSURL {
    
        let urlString = "https://api.weibo.com/oauth2/authorize?client_id=\(clientId)&redirect_uri=\(redirectUri)"
        
        return NSURL(string: urlString)!
    }
    
    
    // MARK: - 加载TOKEN
    func loadAccessToken(code : String , finshed : NetWorkFinishedBack) {
    
        let tokenUrl = "https://api.weibo.com/oauth2/access_token"
        
        // 写得时候注意这里不要写错格式、拼写等ß
        let params = ["client_id" : clientId ,
                      "client_secret" : appSecret ,
                      "grant_type" : "authorization_code" ,
                      "code" : code ,
                      "redirect_uri" : redirectUri]

        // 发送请求
        request(RequestType.POST, urlString: tokenUrl, params: params, finished: finshed)
        
    }
    
    // MARK: - 检查并生成token字典
    private func tokenDict(finished : NetWorkFinishedBack) -> [String : AnyObject]? {
    
        // 判断access_token是否存在，不存在直接return nil
        if UserAccount.loadAccount()?.access_token == nil {
            
            // 错误回调
            let error = ZJNetworkError.networkTokenError.error()
            
            finished(result: nil, error: error)
            
            print(error)
            
            return nil
        }
        
        // 生成token字典返回
        return ["access_token" : UserAccount.loadAccount()!.access_token!]

    }
    
    
    // MARK: - 发送微博
    func sendStatus(status : String , image : UIImage? , finished : NetWorkFinishedBack) {
        
        // 检查token是否存在
        guard var params = tokenDict(finished) else {
            
            return
            
        }
        
        params["status"] = status
        
        // POST发布微博
        // 判断是否有图片
        if image == nil {
        
            // 没有图片则只发布文字微博
            request(RequestType.POST, urlString: "2/statuses/update.json", params: params, finished: finished)
            
        } else {
        
            uploadImage("https://upload.api.weibo.com/2/statuses/upload.json", image: image!, params: params, finished: finished)
            
        }
        

    }
    
    
   
    // MARK: - 加载微博数据
    /// since_id : 若指定此参数，则返回ID比since_id大的微博（即比since_id时间晚的微博），默认为0。
    /// max_id  : 若指定此参数，则返回ID小于或等于max_id的微博，默认为0。 
    
    func loadStatus(since_id : Int , max_id : Int , finished : NetWorkFinishedBack) {
    
        let statusUrl = "2/statuses/home_timeline.json"
        
        // 判断token是否存在，为空直接返回
        // guard let 与 if let 刚好相反，守卫 var也可以
        // let 获得的变量后面可以解释使用，且后面使用时一定有值
        guard var params = tokenDict(finished) else {
        
            // params再此为nil，直接返回
            return
            
        }
        
        // 判断两个参数是否有值
        if since_id > 0 {
        
            params["since_id"] = since_id
        }
        
        if max_id > 0 {
        
            params["max_id"] = max_id - 1
        }
        
        request(RequestType.GET, urlString: statusUrl, params: params, finished: finished)

    }
    
    
    // MARK: - 加载用户信息
    // 职责：做网络访问，获取网络返回的字典
    func loadUsersInfo(uid : String , finished : NetWorkFinishedBack) {
    
        // 获取url
        let urlString = "2/users/show.json"
        
        guard var params = tokenDict(finished) else {
        
            return
        }
        
        params["uid"] = uid
        
        // 发送请求
        request(RequestType.GET, urlString: urlString, params: params, finished: finished)
        
    }
    
    // MARK: - 成功、失败请求
    private func successRequest(finished : NetWorkFinishedBack) -> (NSURLSessionDataTask! , AnyObject!) -> Void {
    
        // 成功请求
        let suessed : (NSURLSessionDataTask! , AnyObject!) -> Void = { (_, JSON) -> Void in
            
            // 成功的回调，此时result肯定有值
            if let result = JSON as? [String : AnyObject] {
                
                // 调用finished
                finished(result: result, error: nil)
                
            }
            else {
                
                // 成功回调但是没有数据
                // 没有数据，创建一个error，实际开发中，error必须有回调
                // 调用finished
                let error = ZJNetworkError.networkDataError.error()
                
                finished(result: nil, error: error)
            }
        }
        
        return suessed

    }
    
    private func failedRequest(finished : NetWorkFinishedBack) -> (NSURLSessionDataTask! , NSError!) -> Void {
    
        // 失败请求
        let failed : (NSURLSessionDataTask! , NSError!) -> Void = { (_, error) -> Void in
            
            print(error)
            
            finished(result: nil, error: error)
        }
        
        return failed
        
    }
    
    
    // MARK: - 上传图像的网络方法
    private func uploadImage(urlString : String , image : UIImage , params : [String : AnyObject] , finished : NetWorkFinishedBack) {
    
        // POST请求
        // urlString 地址   image  图像   params 参数字典   finished  完成回调   formData  构造数据体
        POST(urlString, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
            
            // 将image转为二进制数据
            let data = UIImagePNGRepresentation(image)!
            
            // 2. 构造数据体
            /**
            name: 服务器要求的字段名   这里是pic
            fileName: 保存在服务器的文件名，提示：如果接口文档没有指定文件名参数，可以随便写
            mimeType: 告诉服务器上传文件的类型，如果不想告诉服务器，可以使用 application/octet-stream
            格式：大类/小类
            常见的格式：image/png ，image/jpg ，image/gif ，text/html ，text/plain
            */
            formData.appendPartWithFileData(data, name: "pic", fileName: "xxx", mimeType: "application/octet-stream")
            
            }, success: successRequest(finished), failure: failedRequest(finished))
        
    }
    
    
    // MARK: - 封装AFN网络方法，让程序与第三方框架隔离
    // requestType : 前面定义的枚举中选择网络请求的类型
    // urlString : URL地址  params : 参数字典  finished : 完成回调
    private func request(requestType: RequestType , urlString : String , params : [String : AnyObject] , finished : NetWorkFinishedBack) {
        
        // 根据枚举的值选择执行的方法
        switch requestType {
        
        case.GET:
            
            GET(urlString, parameters: params, success: successRequest(finished), failure: failedRequest(finished))
            
        case.POST:
            
            POST(urlString, parameters: params, success: successRequest(finished), failure: failedRequest(finished))
        }

    }
    
}
